import { TargetType } from '../../classes/interface';
import type {
  DropTarget,
  DropTargetHookSpec,
  DropTargetMonitor,
} from '../dropInterface';

class DropTargetImpl<
  DragItem = unknown,
  DropResult = unknown,
  CollectedProps = unknown,
> implements DropTarget
{
  private spec: DropTargetHookSpec<DragItem, DropResult, CollectedProps>;
  private monitor: DropTargetMonitor<DragItem, DropResult>;

  constructor(
    spec: DropTargetHookSpec<DragItem, DropResult, CollectedProps>,
    monitor: DropTargetMonitor<DragItem, DropResult>,
  ) {
    this.spec = spec;
    this.monitor = monitor;
  }

  public setSpec(
    newSpec: DropTargetHookSpec<DragItem, DropResult, CollectedProps>,
  ) {
    this.spec = newSpec;
  }
  public getAccept: () => TargetType = () => {
    return this.spec.accept;
  };

  public canDrop: () => boolean = () => {
    const spec = this.spec;
    const monitor = this.monitor;
    if (typeof spec.canDrop === 'boolean') {
      return spec.canDrop;
    } else if (typeof spec.canDrop === 'function') {
      return spec.canDrop(this.monitor.getItem(), monitor);
    } else {
      return true;
    }
  };

  public hover() {
    const spec = this.spec;
    const monitor = this.monitor;
    if (spec.hover) {
      spec.hover(monitor.getItem(), monitor);
    }
  }

  public drop(): DropResult | undefined {
    const spec = this.spec;
    const monitor = this.monitor;
    if (spec.drop) {
      return spec.drop(monitor.getItem(), monitor);
    }
    return;
  }

  public getDropNode(): HTMLElement | null {
    return this.spec.dropNode;
  }
}

export default DropTargetImpl;
